/* File: ast_expr.cc
 * -----------------
 * Implementation of expression node classes.
 */

#include <string.h>
#include "ast_expr.h"
#include "ast_type.h"
#include "ast_decl.h"
#include "errors.h"

Type *ArithmeticExpr::getType() {
   Type *rtype = right->getType();
   
   if (left == NULL) {
      if (rtype->equals(Type::intType) ||
          rtype->equals(Type::doubleType))
         return rtype;
      else
         return Type::errorType;
   }
   
   Type *ltype = left->getType();
   
   if (ltype->equals(Type::intType) &&
       rtype->equals(Type::intType))
      return ltype;
   
   if (ltype->equals(Type::doubleType) &&
       rtype->equals(Type::doubleType))
      return ltype;
   
   return Type::errorType;
}

Type *EmptyExpr::getType() {
   return Type::errorType;
}

void ArithmeticExpr::check() {
   if (left != NULL)
      left->check();
   
   right->check();
   
   Type *rtype = right->getType();
   
   if (left == NULL) {
      if (rtype->equals(Type::intType) ||
          rtype->equals(Type::doubleType))
         return;
      else
         ReportError::IncompatibleOperand(op, rtype);
      
      return;
   }
   
   Type *ltype = left->getType();
   
   if (ltype->equals(Type::intType) &&
       rtype->equals(Type::intType))
      return;
   
   if (ltype->equals(Type::doubleType) &&
       rtype->equals(Type::doubleType))
      return;
   
   ReportError::IncompatibleOperands(op, ltype, rtype);
}

Type *RelationalExpr::getType() {
   Type *rtype = right->getType();
   Type *ltype = left->getType();
   
   if (ltype->equals(Type::intType) &&
       rtype->equals(Type::intType))
      return Type::errorType;
   
   if (ltype->equals(Type::doubleType) &&
       rtype->equals(Type::doubleType))
      return Type::errorType;
   
   return Type::boolType;
}

void RelationalExpr::check() {
   Type *rtype = right->getType();
   Type *ltype = left->getType();
   
   if (ltype->equals(Type::intType) &&
       rtype->equals(Type::intType))
      return;
   
   if (ltype->equals(Type::doubleType) &&
       rtype->equals(Type::doubleType))
      return;
   
   ReportError::IncompatibleOperands(op, ltype, rtype);
}

Type *EqualityExpr::getType() {
   Type *rtype = right->getType();
   Type *ltype = left->getType();
   
   if (ltype->equals(Type::intType) &&
       rtype->equals(Type::intType))
      return Type::errorType;
   
   if (ltype->equals(Type::doubleType) &&
       rtype->equals(Type::doubleType))
      return Type::errorType;
   
   return Type::boolType;
}

void EqualityExpr::check(){
   left->check();
   right->check();
   
   Type *rtype = right->getType();
   Type *ltype = left->getType();
   
   if (!rtype->equals(ltype) &&
       !ltype->equals(rtype))
      ReportError::IncompatibleOperands(op, ltype, rtype);
}

Type *LogicalExpr::getType() {
   Type *rtype = right->getType();
   
   if (left == NULL) {
      if (rtype->equals(Type::boolType))
         return Type::boolType;
      else
         return Type::errorType;
   }
   
   Type *ltype = left->getType();
   
   if (ltype->equals(Type::boolType) &&
       rtype->equals(Type::boolType))
      return Type::boolType;
   
   return Type::errorType;
}

void LogicalExpr::check() {
   if (left != NULL)
      left->check();
   
   right->check();
   
   Type *rtype = right->getType();
   
   if (left == NULL) {
      if (rtype->equals(Type::boolType))
         return;
      else
         ReportError::IncompatibleOperand(op, rtype);
      
      return;
   }
   
   Type *ltype = left->getType();
   
   if (ltype->equals(Type::boolType) &&
       rtype->equals(Type::boolType))
      return;
   
   ReportError::IncompatibleOperands(op, ltype, rtype);
}

Type *AssignExpr::getType() {
   Type *ltype = left->getType();
   Type *rtype = right->getType();
   
   if (!rtype->equals(ltype))
      return Type::errorType;
   
   return ltype;
}

void AssignExpr::check() {
	//cout << "in here" << endl;
   left->check();
   right->check();
	
   Type *ltype = left->getType();
   Type *rtype = right->getType();
	//cout << "left: " << ltype->getName() << " right: " << rtype->getName() << endl;
   Decl *d = Program::gScope->map->Lookup(rtype->getName());
   ClassDecl *c = dynamic_cast<ClassDecl*>(d);
	
	while(c != NULL) {
		Type *e = c->getExtends();
		if(e == NULL) {
         List<NamedType*>* l = c->getImplements();
         for(int i = 0; i < l->NumElements(); i++) {
            NamedType* m = l->Nth(i);
            if(ltype->equals(m)) {
               return;
            }
         }
			if(e == NULL) {
				break;
			}
			//printf("da null");
		}
		//cout << "type: " << e->getName() << endl;
		if(ltype->equals(e)) {
			//printf("hewwo %s", e->getName());
			return;
		}
		Decl *dd = Program::gScope->map->Lookup(e->getName());
		ClassDecl *cc = dynamic_cast<ClassDecl*>(dd);
		c = cc;
	}
   
   if (! (rtype->equals(ltype) ) && !ltype->equals(Type::errorType)) {
      ReportError::IncompatibleOperands(op, ltype, rtype);
	}
}

Type *This::getType() {
   ClassDecl *d = getClassDecl(scope);
   if (d == NULL)
      return Type::errorType;
   
   return d->getType();
}

void This::check() {
   if (getClassDecl(scope) == NULL)
      ReportError::ThisOutsideClassScope(this);
}

IntConstant::IntConstant(yyltype loc, int val) : Expr(loc) {
   value = val;
}

Type *IntConstant::getType() {
   return Type::intType;
}

DoubleConstant::DoubleConstant(yyltype loc, double val) : Expr(loc) {
   value = val;
}

Type *DoubleConstant::getType() {
   return Type::doubleType;
}

BoolConstant::BoolConstant(yyltype loc, bool val) : Expr(loc) {
   value = val;
}

Type *BoolConstant::getType() {
   return Type::boolType;
}

StringConstant::StringConstant(yyltype loc, const char *val) : Expr(loc) {
   Assert(val != NULL);
   value = strdup(val);
}

Type *StringConstant::getType() {
   return Type::stringType;
}

Type *NullConstant::getType() {
   return Type::nullType;
}

Operator::Operator(yyltype loc, const char *tok) : Node(loc) {
   Assert(tok != NULL);
   strncpy(tokenString, tok, sizeof(tokenString));
}
CompoundExpr::CompoundExpr(Expr *l, Operator *o, Expr *r) 
: Expr(Join(l->GetLocation(), r->GetLocation())) {
   Assert(l != NULL && o != NULL && r != NULL);
   (op=o)->SetParent(this);
   (left=l)->SetParent(this); 
   (right=r)->SetParent(this);
}

CompoundExpr::CompoundExpr(Operator *o, Expr *r) 
: Expr(Join(o->GetLocation(), r->GetLocation())) {
   Assert(o != NULL && r != NULL);
   left = NULL; 
   (op=o)->SetParent(this);
   (right=r)->SetParent(this);
}

void CompoundExpr::useScope(Scope *s) {
   scope->setParent(s);
   
   if (left != NULL)
      left->useScope(scope);
   
   right->useScope(scope);
}

void CompoundExpr::check() {
   if (left != NULL)
      left->check();
   
   right->check();
}

ArrayAccess::ArrayAccess(yyltype loc, Expr *b, Expr *s) : LValue(loc) {
   (base=b)->SetParent(this); 
   (subscript=s)->SetParent(this);
}

Type *ArrayAccess::getType() {
   ArrayType *t = dynamic_cast<ArrayType*>(base->getType());
   if (t == NULL)
      return Type::errorType;
   
   return t->getElemType();
}

void ArrayAccess::useScope(Scope *s) {
   scope->setParent(s);
   
   base->useScope(scope);
   subscript->useScope(scope);
}

void ArrayAccess::check(){
   base->check();
   subscript->check();
   
   ArrayType *t = dynamic_cast<ArrayType*>(base->getType());
   if (t == NULL)
      ReportError::BracketsOnNonArray(base);
   
   if (!subscript->getType()->equals(Type::intType))
      ReportError::SubscriptNotInteger(subscript);
}

FieldAccess::FieldAccess(Expr *b, Identifier *f) 
: LValue(b? Join(b->GetLocation(), f->GetLocation()) : *f->GetLocation()) {
   Assert(f != NULL); // b can be be NULL (just means no explicit base)
   base = b; 
   if (base) base->SetParent(this); 
   (field=f)->SetParent(this);
}

Type *FieldAccess::getType() {
   Decl *d;
   ClassDecl *c;
   Type *t;
   
   c = getClassDecl(scope);
   
   if (base == NULL) {
      if (c == NULL) {
         d = getFieldDecl(field, scope);
      } else {
         t = c->getType();
         d = getFieldDecl(field, t);
      }
   } else {
      t = base->getType();
      d = getFieldDecl(field, t);
   }
   
   if (d == NULL)
      return Type::errorType;
   
   if (dynamic_cast<VarDecl*>(d) == NULL)
      return Type::errorType;
   
   return static_cast<VarDecl*>(d)->getType();
}

void FieldAccess::useScope(Scope *s) {
   scope->setParent(s);
   
   if (base != NULL)
      base->useScope(scope);
}

void FieldAccess::check() {
   if (base != NULL)
      base->check();
   
   Decl *d;
   Type *t;
   
   if (base == NULL) {
      ClassDecl *c = getClassDecl(scope);
      if (c == NULL) {
         if ((d = getFieldDecl(field, scope)) == NULL) {
            ReportError::IdentifierNotDeclared(field, LookingForVariable);
            return;
         }
      } else {
         t = c->getType();
         if ((d = getFieldDecl(field, t)) == NULL) {
            ReportError::FieldNotFoundInBase(field, t);
            return;
         }
      }
   } else {
      t = base->getType();
      if ((d = getFieldDecl(field, t)) == NULL) {
         ReportError::FieldNotFoundInBase(field, t);
         return;
      }
      else if (getClassDecl(scope) == NULL) {
         ReportError::InaccessibleField(field, t);
         return;
      }
   }
   
   if (dynamic_cast<VarDecl*>(d) == NULL) {
		ReportError::IdentifierNotDeclared(field, LookingForVariable);
	}
}

Call::Call(yyltype loc, Expr *b, Identifier *f, List<Expr*> *a) : Expr(loc)  {
   Assert(f != NULL && a != NULL); // b can be be NULL (just means no explicit base)
   base = b;
   if (base) base->SetParent(this);
   (field=f)->SetParent(this);
   (actuals=a)->SetParentAll(this);
}

Type *Call::getType() {
   Decl *d;
   
   if (base == NULL) {
      ClassDecl *c = getClassDecl(scope);
      if (c == NULL) {
         if ((d = getFieldDecl(field, scope)) == NULL)
            return Type::errorType;
      } else {
         if ((d = getFieldDecl(field, c->getType())) == NULL)
            return Type::errorType;
      }
   } else {
      Type *t = base->getType();
      if ((d = getFieldDecl(field, t)) == NULL) {
         
         if (dynamic_cast<ArrayType*>(t) != NULL &&
             strcmp("length", field->getName()) == 0)
            return Type::intType;
         
         return Type::errorType;
      }
   }
   
   if (dynamic_cast<FnDecl*>(d) == NULL)
      return Type::errorType;
   
   return static_cast<FnDecl*>(d)->getReturnType();
}

void Call::useScope(Scope *s) {
   scope->setParent(s);
   
   if (base != NULL)
      base->useScope(scope);
   
   for (int i = 0, n = actuals->NumElements(); i < n; ++i)
      actuals->Nth(i)->useScope(scope);
}

void Call::check() {
   if (base != NULL)
      base->check();
   
   Decl *d;
   Type *t;
   
   if (base == NULL) {
      ClassDecl *c = getClassDecl(scope);
      if (c == NULL) {
         if ((d = getFieldDecl(field, scope)) == NULL) {
            checkParams(d);
            ReportError::IdentifierNotDeclared(field, LookingForFunction);
            return;
         }
      } else {
         t = c->getType();
         if ((d = getFieldDecl(field, t)) == NULL) {
            checkParams(d);
            ReportError::IdentifierNotDeclared(field, LookingForFunction);
            return;
         }
      }
   } else {
      t = base->getType();
      if ((d = getFieldDecl(field, t)) == NULL) {
         checkParams(d);
         
         if (dynamic_cast<ArrayType*>(t) == NULL ||
             strcmp("length", field->getName()) != 0)
            ReportError::FieldNotFoundInBase(field, t);
         
         return;
      }
   }
   
   checkParams(d);
}

void Call::checkParams(Decl *d){
   for (int i = 0, n = actuals->NumElements(); i < n; ++i)
      actuals->Nth(i)->check();
   
   FnDecl *fnDecl = dynamic_cast<FnDecl*>(d);
   if (fnDecl == NULL)
      return;
   
   List<VarDecl*> *formals = fnDecl->getFormals();
   
   int numExpected = formals->NumElements();
   int numGiven = actuals->NumElements();
   if (numExpected != numGiven) {
      ReportError::NumArgsMismatch(field, numExpected, numGiven);
      return;
   }
   
   for (int i = 0, n = actuals->NumElements(); i < n; ++i) {
      Type *given = actuals->Nth(i)->getType();
      Type *expected = formals->Nth(i)->getType();
      if (!given->equals(expected))
         ReportError::ArgMismatch(actuals->Nth(i), i+1, given, expected);
   }
}

NewExpr::NewExpr(yyltype loc, NamedType *c) : Expr(loc) { 
   Assert(c != NULL);
   (cType=c)->SetParent(this);
}

Type *NewExpr::getType() {
   Decl *d = Program::gScope->map->Lookup(cType->getName());
   ClassDecl *c = dynamic_cast<ClassDecl*>(d);
   
   if (c == NULL) {
      return Type::errorType;
   }
   
   return c->getType();
}

void NewExpr::check() {
   Decl *d = Program::gScope->map->Lookup(cType->getName());
   ClassDecl *c = dynamic_cast<ClassDecl*>(d);
   
   if (c == NULL)
      ReportError::IdentifierNotDeclared(cType->getId(), LookingForClass);
}

NewArrayExpr::NewArrayExpr(yyltype loc, Expr *sz, Type *et) : Expr(loc) {
	//fprintf(stderr, "1");
   Assert(sz != NULL && et != NULL);
	//fprintf(stderr, "2");
   (size=sz)->SetParent(this);
	//fprintf(stderr, "3");
   (elemType=et)->SetParent(this);
   //fprintf(stderr, "4");
}

Type *NewArrayExpr::getType() {
   //	fprintf(stderr, "5");
   return new ArrayType(elemType);
	//fprintf(stderr, "6");
}

void NewArrayExpr::useScope(Scope *s) {
	//fprintf(stderr, "7");
   scope->setParent(s);
   //fprintf(stderr, "8");
   size->useScope(scope);
	//fprintf(stderr, "9");
}

void NewArrayExpr::check(){
	//fprintf(stderr, "10");
   size->check();
   //fprintf(stderr, "\n11");
   if (!size->getType()->equals(Type::intType))
      ReportError::NewArraySizeNotInteger(size);
   //fprintf(stderr, "\n12");
   if (elemType->checkPrim() && !elemType->equals(Type::voidType))
      return;
	ArrayType *t;
	if ((t = dynamic_cast<ArrayType*>(elemType)) != NULL && t->getElemType()->checkPrim())
		return;
   //fprintf(stderr, "\n13");
   Decl *d = Program::gScope->map->Lookup(elemType->getName());
   //fprintf(stderr, "\n14");
   if (dynamic_cast<ClassDecl*>(d) == NULL) {
		//fprintf(stderr, "\n name: %s", elemType->getName());
      ReportError::IdentifierNotDeclared(new Identifier(*(elemType->GetLocation()),elemType->getName()), LookingForType);
   }
   //fprintf(stderr, "\n15");
}


Type *ReadIntegerExpr::getType() {
   return Type::intType;
}

Type *ReadLineExpr::getType() {
   return Type::stringType;
}

ClassDecl* Expr::getClassDecl(Scope *s) {
   while (s != NULL) {
      ClassDecl *d;
      if ((d = s->getClassDecl()) != NULL)
         return d;
      s = s->getParent();
   }
   
   return NULL;
}

Decl* Expr::getFieldDecl(Identifier *f, Type *b) {
   NamedType *t = dynamic_cast<NamedType*>(b);
   
   while (t != NULL) {
      Decl *d = Program::gScope->map->Lookup(t->getName());
      ClassDecl *c = dynamic_cast<ClassDecl*>(d);
      InterfaceDecl *i = dynamic_cast<InterfaceDecl*>(d);
      
      Decl *fieldDecl;
      if (c != NULL) {
         if ((fieldDecl = getFieldDecl(f, c->getScope())) != NULL)
            return fieldDecl;
         else
            t = c->getExtends();
      } else if (i != NULL) {
         if ((fieldDecl = getFieldDecl(f, i->getScope())) != NULL)  {
            return fieldDecl;
         }
         else {
            t = NULL;
         }
      } else {
         t = NULL;
      }
   }
   
   return getFieldDecl(f, scope);
}

Decl* Expr::getFieldDecl(Identifier *f, Scope *s) {
   while (s != NULL) {
      Decl *lookup;
      if ((lookup = s->map->Lookup(f->getName())) != NULL)
         return lookup;
      
      s = s->getParent();
   }
   
   return NULL;
}